﻿using System;
using System.Globalization;
using System.Windows.Input;

namespace Foundation.Markup
{
    public class Command : MarkupExtension
    {
        public Command() : base(null) // using of DataContext
        {
        }

        public string Key { get; set; }

        public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var viewModel = value as Foundation.ViewModel;
            if (viewModel == null || Key == null) return null;
            var command = SharedCommands.Get(Key);
            var commandBinding = viewModel[command];
            var commandContainer = new CommandContainer(commandBinding);
            return commandContainer;
        }
    }

    public class CommandContainer : ICommand
    {
        private readonly WeakReference _weakReference;

        public CommandContainer(CommandBinding commandBinding)
        {
            _weakReference = new WeakReference(commandBinding);
            _sharedCommand = commandBinding.Command;
            _sharedCommand.CanExecuteChanged += (sender, args) => 
                CanExecuteChanged(sender, args);
        }

        private readonly ICommand _sharedCommand;
        private bool _lastCanExecute;

        public bool CanExecute(object parameter)
        {
            var commandBinding = _weakReference.Target as CommandBinding;
            if (commandBinding == null)
            {
                _sharedCommand.CanExecuteChanged -= CanExecuteChanged;
                return false;
            }
            var canExecuteEventArgs = new CanExecuteEventArgs(commandBinding.Command, parameter);
            commandBinding.OnPreviewCanExecute(this, canExecuteEventArgs);
            if (!canExecuteEventArgs.CanExecute) return false;
            commandBinding.OnCanExecute(this, canExecuteEventArgs);
            if (_lastCanExecute != canExecuteEventArgs.CanExecute)
            {
                _lastCanExecute = canExecuteEventArgs.CanExecute;
                CanExecuteChanged(this, EventArgs.Empty);
            }

            return canExecuteEventArgs.CanExecute;
        }

        public void Execute(object parameter)
        {
            var commandBinding = _weakReference.Target as CommandBinding;
            if (commandBinding == null) return;
            commandBinding.OnPreviewExecuted(this, new ExecutedEventArgs(commandBinding.Command, parameter));
            commandBinding.OnExecuted(this, new ExecutedEventArgs(commandBinding.Command, parameter));
            SharedCommands.UpdateCanExecuteState();
        }

        public event EventHandler CanExecuteChanged = (sender, args) => { };
    }
}
